#pragma once

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <arpa/inet.h>
// 包含此头文件才可以使用sockaddr_in结构体
#include "log.hpp"
#include "err.hpp"

const static int gbacklog = 32;
const static int defaultsock = -1;

class Sock
{

public:
    Sock()
        : _listensock(defaultsock)
    {
    }
    ~Sock()
    {
        if (_listensock != defaultsock)
            close(_listensock);
    }

public:
    void Socket()
    {

        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock == -1)
        {
            logMessage(FATAL, "create socket error!");
            exit(SOCKET_ERR);
        }
        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        logMessage(NORMAL, "create socket success: %d", _listensock);
    }
    void Bind(int port)
    {
        struct sockaddr_in local; // 服务器本地的网络信息
        memset(&local, 0, sizeof(local));
        local.sin_family = PF_INET; // IP protocol family
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY; // 任意ip

        if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) == -1)
        {
            logMessage(FATAL, "bind socket error!");
            exit(BIND_ERR);
        }
        logMessage(NORMAL, "bind socket success");
    }

    void Listen()
    {
        if (listen(_listensock, gbacklog) == -1) // 第二个参数gbacklog后面再讲
        {
            logMessage(FATAL, "listen socket error!");
            exit(LISTEN_ERR);
        }
        logMessage(NORMAL, "listen socket success");
    }

    int Accept(std::string *clientip, uint16_t *clientport, int *errcode)
    {
        struct sockaddr_in peer; // 远端的客户端信息
        socklen_t len = sizeof(peer);
        int sockfd = accept(_listensock, (struct sockaddr *)&peer, &len); // sockfd是未来和client进行通信的套接字
        *errcode = errno;
        if (sockfd == -1)
        {
            logMessage(ERROR, "accept error, error_string: %s", strerror(errno));
            // 这是一种错误,但这并不影响server和其他的client继续连接然后通信啊,当前这个客户端无法连接成功,不代表其他client也连接不成功啊!
        }
        else
        {
            logMessage(NORMAL, "accept a new link success, get new sockfd: %d", sockfd);
            *clientport = ntohs(peer.sin_port);
            *clientip = inet_ntoa(peer.sin_addr);
        }
        return sockfd;
    }
    int FD()
    {
        return _listensock;
    }
    void Close()
    {
        if(_listensock != defaultsock)
            close(_listensock);
    }

private:
    int _listensock;
};